/* 
 * File:   DynamicLibrary.h
 * Author: pkv
 * 
 * Created on July 28, 2011, 9:00 PM
 * 
 * Cross-platform library loading code.
 * 
 * Adapted from: http://www.codeproject.com/KB/architecture/plat_ind_coding.aspx
 *
 */

#ifndef DYNAMIC_LIBRARY_H
#define	DYNAMIC_LIBRARY_H

#include <string>

// Define platform-specific includes and export declarations
#ifdef _WIN32
    // Windows (x64 and x86)    
    #include <windows.h>
#elif __APPLE__
    // Mac OS
    #include <dlfcn.h>
#elif (__unix__ || __posix__ || linux)
    // *NIX
    #include <dlfcn.h>
#else
    #error No known compiler defined by preprocessor
#endif

/**
 * Takes a library basename and optionally a file path and attempts to form a 
 * platform specific path to the library.
 * 
 * Assume a library name of Foo, the following library filenames are generated:
 * Windows: Foo.dll
 * Mac OSX: libFoo.dylib
 * *NIX: libFoo.so
 * 
 * @param basename the name of the shared library to be loaded
 * @param path the directory in which the shared library resides
 * @return 
 */
std::string GetLibraryName(const std::string baseName)
{
#ifdef _WIN32
    // Windows (x64 and x86)
    return baseName + ".dll";
#elif __APPLE__
    // Mac OS
    return "lib" + baseName + ".dylib";
#elif (__unix__ || __posix__ || linux)
    // *NIX
    return "lib" + baseName + ".so";
#endif
}

/**
 * Loads a shared library from a specific file path, or the system library path
 * if only a filename is specified, using the specified linking mode.  
 * The library name is NOT adapted in any way.  The exact
 * filename that is specified will be loaded.
 * 
 * @param libPath the path to a shared library to be loaded
 * @param iMode the linking mode that will be used
 * @return a pointer to the shared library object, or NULL on failure.
 */
void* LoadSharedLibraryFile(const std::string libPath, int iMode = 2)
{   
#ifdef _WIN32
    // Windows (x64 and x86)
    return (void*)LoadLibrary(libPath.c_str());
#elif __APPLE__
    // Mac OS
    return dlopen(libPath.c_str(), iMode);
#elif (__unix__ || __posix__ || linux)
    // *NIX
    return dlopen(libPath.c_str(), iMode);
#endif
}

/**
 * Loads a shared library from the system library path, using the specified
 * linking mode.  The library name is adapted to a platform specific filename
 * automatically.
 * 
 * Assume a library name of Foo, the following library filenames are generated:
 * Windows: Foo.dll
 * Mac OSX: libFoo.dylib
 * *NIX: libFoo.so
 * 
 * The following linking modes are available:
 * RTLD_LAZY = 1
 * RTLD_NOW = 2
 * RTLD_GLOBAL = 4
 * 
 * @param libName the name of a shared library to be loaded
 * @param iMode the linking mode that will be used
 * @return a pointer to the shared library object, or NULL on failure.
 */
void* LoadSharedLibrary(const std::string libName, int iMode = 2)
{   
    std::string libFilename = GetLibraryName(libName);
    return LoadSharedLibraryFile(libFilename, iMode);
}

/**
 * Dynamically retrieves a function pointer from a loaded shared library.
 * The function is returned as a (void *), it is up to the calling program to
 * cast the function pointer to the appropriate type.
 * 
 * @param libraryPtr a pointer to a shared library
 * @param functionName the name of a function exported in the shared library
 * @return a function pointer to the specified function, or NULL on failure.
 */
void *GetFunction(void *libraryPtr, const std::string functionName)
{
#ifdef _WIN32
    // Windows (x64 and x86)
    return (void*)GetProcAddress((HINSTANCE)libraryPtr, functionName.c_str());
#elif __APPLE__
    // Mac OS
    return dlsym(libraryPtr, functionName.c_str());
#elif (__unix__ || __posix__ || linux)
    // *NIX
    return dlsym(libraryPtr, functionName.c_str());
#endif
}

/**
 * Unloads a shared library object.
 * 
 * @param libraryPtr a pointer to a shared library
 * @return true if the library was unloaded, and false if an error occurred.
 */
bool UnloadSharedLibrary(void *libraryPtr)
{
#ifdef _WIN32
    // Windows (x64 and x86)
    return FreeLibrary((HINSTANCE)libraryPtr);
#elif __APPLE__
    // Mac OS
    return !dlclose(libraryPtr);
#elif (__unix__ || __posix__ || linux)
    // *NIX
    return !dlclose(libraryPtr);
#endif
}

#endif	/* DYNAMIC_LIBRARY_H */

